package com.qcc.jvm01.classloader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @创建人 qianchengcheng
 * @创建时间 2021/12/31
 * @描述 自定义xlass类加载器
 */
public class XlassLoader extends ClassLoader{

    private static Logger log = LoggerFactory.getLogger(XlassLoader.class);

    private static final String xlassSuffix = ".xlass";

    private static final String methodName = "hello";

    private static final String xlassFileName = "Hello";

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        log.info("XlassLoader.findClass==>进入findClass内部");
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(name.replace(".","/") + xlassSuffix);
        try {
            // 获取可读取的数据大小
            int length = inputStream.available();
            byte[] byteArray = new byte[length];
            inputStream.read(byteArray);
            byte[] decodeBytes = decodeXlass(byteArray);
            return defineClass(name,decodeBytes,0,decodeBytes.length);
        }catch (IOException e){
            throw new ClassNotFoundException("class " + name + "not found");
        }finally {
            if (null != inputStream){
                try {
                    inputStream.close();
                }catch (IOException e){
                    log.error("findClass==>close inputStream exception",e);
                }
            }
        }
    }

    private byte[] decodeXlass(byte[] xlassBytes){
        for (int i = 0 ; i < xlassBytes.length ; i++){
            xlassBytes[i] = (byte) (255 - xlassBytes[i] & 0xFF);
        }
        return xlassBytes;
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
        XlassLoader xlassLoader = new XlassLoader();
        // loadClass 如果父类加载器加载失败则会调用自定义的findClass方法
        Class<?> clazz = xlassLoader.loadClass(xlassFileName);
        Method[] methods = clazz.getDeclaredMethods();
        for (int i = 0 ; i < methods.length ; i++){
            System.out.println(methods[i]);
        }
        // 获取实例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        // 调用指定方法
        Method method = clazz.getMethod(methodName);
        System.out.println(method.invoke(instance));
    }
}
